#include <iostream>
#include <fstream>
#include <iomanip>

#include "..//Z80Core/z80.h"
#include "GenTables.h"
#include "CheckTables.h"

using namespace std;

//#define GENERATE 1

ofstream *of;

unsigned char CalcSZ(byte value) 
{
	return ((!value)?Z_FLAG:(value&S_FLAG));
}

unsigned char CalcSZP(byte value)
{
	int num =   (!(value & S_FLAG))
                +(!(value & Z_FLAG  ))
                +(!(value & X5_FLAG ))
                +(!(value & H_FLAG  ))
                +(!(value & X3_FLAG ))
                +(!(value & P_FLAG  ))
                +(!(value & N_FLAG  ))
                +(!(value & C_FLAG  ));

	unsigned char out = CalcSZ(value);
		
	if (!(num%2)) out |= P_FLAG;

	return out;
}

word CalcDAA(word value)
{
	pair(A,F,AF);

	byte HF, NF, CF, high, low, diff;

	HF = ((value & BIT10)?1:0);
	NF = ((value & BIT9)?1:0);
	CF = ((value & BIT8)?1:0);

	AF.reg.A = (value & 0xFF);
	AF.reg.F = (NF?N_FLAG:0);

	high = ((AF.reg.A & 0xF0) >> 4);
	low = (AF.reg.A & 0x0F);

	diff = 0;

	if (CF) // 5, 6, 9
	{				
		AF.reg.F |= C_FLAG;

		if (low >= 0x0A) diff = 0x66;		// 9
		else
		{
			if (HF) diff = 0x66;			// 6
			else diff = 0x60;				// 5
		}
	}
	else // 1, 2, 3, 4, 7, 8
	{
		if (low <= 0x09) // 1, 2, 3, 4
		{
			if (high <= 0x09) // 1, 2
			{
				if (HF) diff = 0x06; // 2
				else diff = 0x00; // 1
			}
			else // 3, 4
			{
				AF.reg.F |= C_FLAG;
					
				if (HF) diff = 0x66; // 4
				else diff = 0x60; // 3
			}
		}
		else // 7, 8
		{
			if (high <= 0x08) diff = 0x06; // 7
			else // 8
			{
				AF.reg.F |= C_FLAG;
				diff = 0x66;
			}
		}
	}

	if (NF)
	{
		AF.reg.A -= diff;

		if ((HF) && (low <= 0x05)) AF.reg.F |= H_FLAG;
	}
	else
	{
		AF.reg.A += diff;

		if (low >= 0x0A) AF.reg.F |= H_FLAG;
	}

	AF.reg.F |= (CalcSZP(AF.reg.A) | (AF.reg.A & (BIT5|BIT3)));

	return AF.pair;
}

int GenDAA()
{
	pair(A,F,AF);

	int c = 0, line = 0;

	*of << "const word iDAATable[2048] = {" << endl;

	*of << "\t";

	for ( ; c <= 2047 ; ++c, ++line )
	{		
		AF.pair = CalcDAA(c);

		if (!CheckDAA(c & 0xFF,c >> 8,AF.reg.A,AF.reg.F))
		{
			return -1;
		}

		if (!CheckDAA2(c & 0xFF,c >> 8,AF.reg.A,AF.reg.F))
		{
			return -2;
		}

		if (line == DAA_COLS) 
		{
			line = 0;

			*of << endl << "\t";
		}

		*of << hex << showbase << setfill('0') << setw(6) << internal << AF.pair << ((c < 2047)?",\t":"\t");
	}

	*of << endl << "};" << endl << endl;

	return (c - 1);
}

int GenSZ()
{
	unsigned char tmp;

	int c = 0, line = 0;

	*of << "const byte SZTable[256] = {" << endl;

	*of << "\t";

	for ( ; c <= 255 ; ++c, ++line )
	{		
		tmp = CalcSZ(c);

		if (tmp != ZSTable[c])
		{
			return -1;
		}


		if (line == SZ_COLS) 
		{
			line = 0;

			*of << endl << "\t";
		}

		*of << ((tmp & S_FLAG)?"S_FLAG":((tmp & Z_FLAG?"Z_FLAG":"0"))) << ((c < 255)?",\t":"\t");
	}

	*of << endl << "};" << endl << endl;

	return (c - 1);
}

int GenSZP()
{
	unsigned char tmp;

	int c = 0, line = 0;

	*of << "const byte SZPTable[256] = {" << endl;

	*of << "\t";

	for ( ; c <= 255 ; ++c, ++line )
	{		
		tmp = CalcSZP(c);

		if (tmp != PZSTable[c])
		{
			return -1;
		}

		if (line == SZP_COLS) 
		{
			line = 0;

			*of << endl << "\t";
		}

		if (tmp & P_FLAG)
		{
			*of << ((tmp & S_FLAG)?"S_FLAG|P_FLAG":((tmp & Z_FLAG)?"Z_FLAG|P_FLAG":"P_FLAG")) << ((c < 255)?", ":" ");
		}
		else
		{
			*of << ((tmp & S_FLAG)?"S_FLAG":((tmp & Z_FLAG?"Z_FLAG":"0"))) << ((c < 255)?", ":" ");
		}
	}

	*of << endl << "};" << endl << endl;

	return (c - 1);
}

int main(int argc, char** argv) 
{	

#ifdef GENERATE
	of = new ofstream(FILENAME);
	
	cout << "Generating.." << endl;
	cout << "-- SZ... ";
	cout << GenSZ() << endl;

	cout << "Generating.." << endl;
	cout << "-- SZP... ";
	cout << GenSZP() << endl;

	cout << "Generating.." << endl;
	cout << "-- DAA... ";
	cout << GenDAA() << endl;

	of->close();

	cout << "DONE" << endl;
#else
	LastCheck();
#endif

	return 0;
}
